package validator

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/log"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"time"
)

func ValidateDataUni(dataItem *mpcprotocol.SignInput) (bool, error) {

	log.SyslogInfo("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ValidateDataUni, begin",
		"SignInput", dataItem.String())

	sdb, err := GetDB()
	if err != nil {
		log.SyslogErr("GetDataForApproveUni, getting storeman database fail", "err", err.Error())
		return false, mpcprotocol.ErrGetDb
	}

	approvedKey := buildKeyFromDataUni(dataItem, mpcprotocol.MpcApproved)
	_, err = waitKeyFromDB([][]byte{approvedKey})
	if err != nil {
		log.SyslogErr("ValidateDataUni, waitKeyFromDB has fail", "err", err.Error())
		return false, mpcprotocol.ErrWaitApproved
	}

	_, err = sdb.Get(approvedKey)
	if err != nil {
		log.SyslogErr("ValidateDataUni, sdb.Get has fail", "err", err.Error())
		return false, mpcprotocol.ErrGetApproved
	}

	//var byteRev []byte
	//byteRev, err = json.Marshal(&dataItem)
	//if err != nil {
	//	log.SyslogErr("ValidateData, check has fail", "err", err.Error())
	//	return false, mpcprotocol.ErrMarshal
	//}
	//
	//if !bytes.Equal(value, byteRev) {
	//	var dataInDb mpcprotocol.SignInput
	//	var dataRcv mpcprotocol.SignInput
	//
	//	err := json.Unmarshal(value, &dataInDb)
	//	if err != nil {
	//		log.SyslogErr("ValidateData, Unmarshal dataInDb err", err.Error())
	//	}
	//
	//	err = json.Unmarshal(byteRev, &dataRcv)
	//
	//	inDbStr := fmt.Sprintf("%+v", dataInDb)
	//	rcvStr := fmt.Sprintf("%+v", dataRcv)
	//
	//	if err != nil {
	//		log.SyslogErr("ValidateData, Unmarshal dataRcv err", err.Error())
	//	}
	//
	//	log.SyslogErr("ValidateData, bytes not equal", "data in db", inDbStr, "data recv", rcvStr)
	//	return false, mpcprotocol.ErrApprovedNotConsistent
	//}
	log.SyslogInfo("ValidateDataUni successfully", "data", dataItem.String())
	return true, nil

}

func AddValidDataUni(data *mpcprotocol.SignInput) error {
	log.SyslogInfo("AddValidDataUni", "data", data.String())
	return addOneValidDataUni(*data)
}

/// solution two interface
func AddApprovingDataUni(data *mpcprotocol.SignInput) error {
	log.SyslogInfo("AddApprovingDataUni", "data", data.String())
	return addApprovingDataUni(data)
}

func GetDataForApproveUni(duration time.Duration) ([]mpcprotocol.SignInput, error) {
	log.SyslogInfo("GetDataForApproveUni, begin")

	approvingBytes, _ := getForApprove([]byte(mpcprotocol.MpcApprovingKeysUni))

	var approvingData []mpcprotocol.SignInput
	for i := 0; i < len(approvingBytes); i++ {

		var approvingTemp mpcprotocol.SignInputWithStamp
		err := json.Unmarshal(approvingBytes[i], &approvingTemp)
		if err != nil {
			log.SyslogErr("GetDataForApproveUni, Unmarshal approvingTemp", "err", err.Error())
			//return nil, err
			continue
		}

		if approvingTemp.RcvTimeStamp.Add(duration).After(time.Now()) {
			approvingData = append(approvingData, approvingTemp.SignInput)
		}
	}
	log.SyslogInfo("GetDataForApproveUni successfully", "len", len(approvingData))
	for index, value := range approvingData {
		log.SyslogInfo("GetDataForApproveUni successfully", "index", index, "value", value.String())
	}
	return approvingData, nil
}

func ApproveDataUni(approveData []mpcprotocol.SignInput) []error {
	retResult := make([]error, len(approveData))
	for i := 0; i < len(approveData); i++ {
		dataItem := approveData[i]
		log.SyslogInfo("ApproveDataUni", "data", dataItem.String())
		retResult[i] = approveOneDataUni(dataItem)
	}

	return retResult
}

/// internal function for schnorr

func buildKeyBytesFromDataUni(data *mpcprotocol.SignInput) []byte {
	// build the key.
	var buffer bytes.Buffer
	buffer.Write(data.PKBytes[:])             //pk
	buffer.Write(data.Curve)                  //curve
	buffer.Write(data.Alg)                    // alg
	for i := 0; i < len(data.HashData); i++ { //hash
		buffer.Write(data.HashData[i])
	}
	return buffer.Bytes()
}

// status: approving || approved
func buildKeyFromDataUni(data *mpcprotocol.SignInput, status string) []byte {
	// build the key.
	keyBytes := buildKeyBytesFromDataUni(data)
	return buildKeyFromBytes(keyBytes, status)
}

func approveOneDataUni(dataItem mpcprotocol.SignInput) error {
	approvedKey := buildKeyFromDataUni(&dataItem, mpcprotocol.MpcApproved)
	approvingKey := buildKeyFromDataUni(&dataItem, mpcprotocol.MpcApproving)
	approvingKeysKey := []byte(mpcprotocol.MpcApprovingKeysUni)
	value, err := json.Marshal(&dataItem)
	if err != nil {
		log.SyslogErr("approveOneDataUni, json.Marshal fail", "err", err.Error())
		return err
	}

	return approve(approvedKey, approvingKey, approvingKeysKey, value)
}

func addApprovingDataUni(dataItem *mpcprotocol.SignInput) error {

	approvedKey := buildKeyFromDataUni(dataItem, mpcprotocol.MpcApproved)
	approvingKey := buildKeyFromDataUni(dataItem, mpcprotocol.MpcApproving)
	approvingKeysKey := []byte(mpcprotocol.MpcApprovingKeysUni)
	dataItemWithStamp := mpcprotocol.SignInputWithStamp{SignInput: *dataItem, RcvTimeStamp: time.Now()}

	value, err := json.Marshal(&dataItemWithStamp)
	if err != nil {
		log.SyslogErr("addApprovingDataUni, json.Marshal fail", "err", err.Error())
		return err
	}

	return addApproving(approvedKey, approvingKey, approvingKeysKey, value)
}

func addOneValidDataUni(approveData mpcprotocol.SignInput) error {

	sdb, err := GetDB()
	if err != nil {
		log.SyslogErr("addOneValidData, getting storeman database fail", "err", err.Error())
		return err
	}

	key := buildKeyFromDataUni(&approveData, mpcprotocol.MpcApproved)
	exist, err := sdb.Has(key)
	if exist {
		log.SyslogErr("addOneValidData, has in approved db")
		return errors.New("has in approved db")
	}
	if err != nil {
		log.SyslogErr("addOneValidData, sdb.Has error", "err:", err.Error())
		return err
	}
	// in approving db
	// add in approved DB
	//log.SyslogInfo("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& addOneValidData, begin",
	//	"pk", hexutil.Encode(approveData.PKBytes),
	//	"data", hexutil.Encode([]byte(approveData.Data)),
	//	"Extern", hexutil.Encode([]byte(approveData.Extern)))

	log.SyslogInfo("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& addOneValidData, begin", approveData.String())

	val, err := json.Marshal(&approveData)
	if err != nil {
		log.SyslogErr("addOneValidData, marshal fail", "err", err.Error())
		return err
	}

	log.SyslogInfo("=============== approveOneData", "data", approveData.String(), "approved key", hexutil.Encode(key))
	err = addKeyValueToDB(key, val)
	if err != nil {
		log.SyslogErr("addOneValidData, addKeyValueToDB fail", "err", err.Error())
		return err
	}
	return nil
}
